package com.example.examsystem.aspect;

import com.example.examsystem.model.SystemLog;
import com.example.examsystem.service.SystemLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;

/**
 * 系统日志切面
 */
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class LogAspect {
    
    private final SystemLogService systemLogService;
    private final ObjectMapper objectMapper;
    
    /**
     * 记录登录日志
     */
    @AfterReturning(pointcut = "execution(* com.example.examsystem.controller.AuthController.login(..))", returning = "result")
    public void logLogin(JoinPoint joinPoint, Object result) {
        try {
            HttpServletRequest request = getHttpServletRequest();
            Object[] args = joinPoint.getArgs();
            
            SystemLog systemLog = new SystemLog();
            systemLog.setAction("用户登录");
            systemLog.setResource("auth");
            systemLog.setDescription("用户登录系统");
            systemLog.setLevel("INFO");
            systemLog.setIpAddress(getClientIpAddress(request));
            systemLog.setUserAgent(request.getHeader("User-Agent"));
            systemLog.setCreatedAt(LocalDateTime.now());
            
            // 从请求参数中获取用户名
            if (args.length > 0 && args[0] != null) {
                String requestBody = objectMapper.writeValueAsString(args[0]);
                if (requestBody.contains("username")) {
                    // 解析用户名
                    systemLog.setUsername(extractUsernameFromRequest(requestBody));
                }
            }
            
            systemLogService.saveLog(systemLog);
        } catch (Exception e) {
            log.error("记录登录日志失败", e);
        }
    }
    
    /**
     * 记录CRUD操作日志
     */
    @AfterReturning(pointcut = "execution(* com.example.examsystem.controller.*Controller.create*(..)) || " +
                              "execution(* com.example.examsystem.controller.*Controller.update*(..)) || " +
                              "execution(* com.example.examsystem.controller.*Controller.delete*(..))")
    public void logCrudOperations(JoinPoint joinPoint) {
        try {
            String methodName = joinPoint.getSignature().getName();
            String className = joinPoint.getTarget().getClass().getSimpleName();
            
            SystemLog systemLog = createBaseLog();
            systemLog.setAction(getActionFromMethod(methodName));
            systemLog.setResource(getResourceFromController(className));
            systemLog.setDescription(String.format("执行%s操作", getActionFromMethod(methodName)));
            systemLog.setLevel("INFO");
            
            systemLogService.saveLog(systemLog);
        } catch (Exception e) {
            log.error("记录CRUD操作日志失败", e);
        }
    }
    
    /**
     * 记录导入导出操作日志
     */
    @AfterReturning(pointcut = "execution(* com.example.examsystem.controller.*Controller.import*(..)) || " +
                              "execution(* com.example.examsystem.controller.*Controller.export*(..))")
    public void logImportExportOperations(JoinPoint joinPoint) {
        try {
            String methodName = joinPoint.getSignature().getName();
            String className = joinPoint.getTarget().getClass().getSimpleName();
            
            SystemLog systemLog = createBaseLog();
            systemLog.setAction(methodName.contains("import") ? "数据导入" : "数据导出");
            systemLog.setResource(getResourceFromController(className));
            systemLog.setDescription(String.format("执行%s操作", systemLog.getAction()));
            systemLog.setLevel("INFO");
            
            systemLogService.saveLog(systemLog);
        } catch (Exception e) {
            log.error("记录导入导出操作日志失败", e);
        }
    }
    
    /**
     * 记录异常日志
     */
    @AfterThrowing(pointcut = "execution(* com.example.examsystem.controller.*Controller.*(..))", throwing = "ex")
    public void logException(JoinPoint joinPoint, Throwable ex) {
        try {
            String methodName = joinPoint.getSignature().getName();
            String className = joinPoint.getTarget().getClass().getSimpleName();
            
            SystemLog systemLog = createBaseLog();
            systemLog.setAction("系统异常");
            systemLog.setResource(getResourceFromController(className));
            systemLog.setDescription(String.format("方法%s执行异常: %s", methodName, ex.getMessage()));
            systemLog.setLevel("ERROR");
            
            systemLogService.saveLog(systemLog);
        } catch (Exception e) {
            log.error("记录异常日志失败", e);
        }
    }
    
    /**
     * 创建基础日志对象
     */
    private SystemLog createBaseLog() {
        HttpServletRequest request = getHttpServletRequest();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        
        SystemLog systemLog = new SystemLog();
        systemLog.setIpAddress(getClientIpAddress(request));
        systemLog.setUserAgent(request.getHeader("User-Agent"));
        systemLog.setCreatedAt(LocalDateTime.now());
        
        if (authentication != null && authentication.isAuthenticated()) {
            systemLog.setUsername(authentication.getName());
            // 如果有用户ID，也可以设置
            // systemLog.setUserId(getCurrentUserId());
        }
        
        return systemLog;
    }
    
    /**
     * 获取HTTP请求对象
     */
    private HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }
    
    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0];
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
    
    /**
     * 从方法名获取操作类型
     */
    private String getActionFromMethod(String methodName) {
        if (methodName.startsWith("create") || methodName.startsWith("add")) {
            return "新增";
        } else if (methodName.startsWith("update") || methodName.startsWith("edit")) {
            return "修改";
        } else if (methodName.startsWith("delete") || methodName.startsWith("remove")) {
            return "删除";
        }
        return methodName;
    }
    
    /**
     * 从控制器名获取资源类型
     */
    private String getResourceFromController(String className) {
        return className.replace("Controller", "").toLowerCase();
    }
    
    /**
     * 从请求体中提取用户名
     */
    private String extractUsernameFromRequest(String requestBody) {
        try {
            // 简单的JSON解析，实际项目中可以使用更完善的方法
            if (requestBody.contains("\"username\"")) {
                int start = requestBody.indexOf("\"username\"") + 11;
                int valueStart = requestBody.indexOf("\"", start) + 1;
                int valueEnd = requestBody.indexOf("\"", valueStart);
                return requestBody.substring(valueStart, valueEnd);
            }
        } catch (Exception e) {
            log.warn("提取用户名失败", e);
        }
        return null;
    }
}
